﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using EnvDTE;
using System.Diagnostics;
using System.Collections;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;

namespace Deb.Drivers
{
    public class VSDriver
    {

        #region Interop imports

        [DllImport("ole32.dll")]
        public static extern int GetRunningObjectTable(int reserved, out IRunningObjectTable prot);

        [DllImport("ole32.dll")]
        public static extern int CreateBindCtx(int reserved, out IBindCtx ppbc);

        #endregion

        //public VSDriver()
        //{
            
        //}

        public string GetDebuggerMode(EnvDTE.DTE ide)
        {
            //string currentMode = String.Empty;
            try
            {
                
                //switch (ide.Debugger.CurrentMode)
                //{
                //    case dbgDebugMode.dbgDesignMode:
                //        Debug.WriteLine("Debugger in DesignMode!");
                //        currentMode = "Debugger in DesignMode!";
                //        break;
                //    case dbgDebugMode.dbgRunMode:
                //        Debug.WriteLine("Debugger in RunMode!");
                //        currentMode = "Debugger in RunMode!";
                //        break;
                //    case dbgDebugMode.dbgBreakMode:
                //        Debug.WriteLine("Debugger in BreakMode!");
                //        currentMode = "Debugger in BreakMode!";
                //        break;
                //    default:
                //        Debug.WriteLine("Debugger mode is unknown!");
                //        currentMode = "Debugger mode is unknown!";
                //        break;
                //}
                ////return currentMode;
                return ide.Debugger.CurrentMode.ToString();
            }
            catch (System.Exception ex)
            {
                Debug.WriteLine("Debugger.CurrentMode Exception: {0}", ex);
                return "";
            }
        }
        

        public void BreakDebugger(EnvDTE.DTE ide)
        {
            ide.Debugger.Break();
        }

        public void GoDebugger(EnvDTE.DTE ide)
        {
            ide.Debugger.Go(false);
        }

        public void StopDebugger(EnvDTE.DTE ide)
        {
            ide.Debugger.Stop(true);
        }

        public void StepIntoDebugger(EnvDTE.DTE ide)
        {
            ide.Debugger.StepInto();
        }

        public void StepOutDebugger(EnvDTE.DTE ide)
        {
            ide.Debugger.StepOut();
        }

        public void StepOverDebugger(EnvDTE.DTE ide)
        {
            ide.Debugger.StepOver();
        }

        /// <summary>
        /// Get a table of the currently running instances of the Visual Studio .NET IDE.
        /// </summary>
        /// <param name="openSolutionsOnly">Only return instances that have opened a solution</param>
        /// <returns>A hashtable mapping the name of the IDE in the running object table to the corresponding DTE object</returns>
        public Hashtable GetIDEInstances(bool openSolutionsOnly)
        {
            Hashtable runningIDEInstances = new Hashtable();
            Hashtable runningObjects = GetRunningObjectTable();

            IDictionaryEnumerator rotEnumerator = runningObjects.GetEnumerator();
            while (rotEnumerator.MoveNext())
            {
                string candidateName = (string)rotEnumerator.Key;
                if (!candidateName.StartsWith("!VisualStudio.DTE"))
                    continue;

                _DTE ide = rotEnumerator.Value as _DTE;
                if (ide == null)
                    continue;

                if (openSolutionsOnly)
                {
                    try
                    {
                        string solutionFile = ide.Solution.FullName;
                        if (solutionFile != String.Empty)
                        {
                            runningIDEInstances[candidateName] = ide;
                        }
                    }
                    catch { }
                }
                else
                {
                    runningIDEInstances[candidateName] = ide;
                }
            }
            return runningIDEInstances;
        }

        public Hashtable GetMultiIDEInstances(bool openSolutionsOnly)
        {
            Hashtable runningIDEInstances = new Hashtable();
            Hashtable runningObjects = GetRunningObjectTable();

            IDictionaryEnumerator rotEnumerator = runningObjects.GetEnumerator();
            while (rotEnumerator.MoveNext())
            {
                string candidateName = (string)rotEnumerator.Key;
                //if (!candidateName.StartsWith("!VisualStudio.DTE"))
                //    continue;

                _DTE ide = rotEnumerator.Value as _DTE;
                if (ide == null)
                    continue;

                if (openSolutionsOnly)
                {
                    try
                    {
                        string solutionFile = ide.Solution.FullName;
                        if (solutionFile != String.Empty)
                        {
                            runningIDEInstances[candidateName] = ide;
                        }
                    }
                    catch { }
                }
                else
                {
                    runningIDEInstances[candidateName] = ide;
                }
            }
            return runningIDEInstances;
        }

        /// <summary>
        /// Get a snapshot of the running object table (ROT).
        /// </summary>
        /// <returns>A hashtable mapping the name of the object in the ROT to the corresponding object</returns>
        //[STAThread]
        public static Hashtable GetRunningObjectTable()
        {
            Hashtable result = new Hashtable();

            IRunningObjectTable runningObjectTable;
            IEnumMoniker monikerEnumerator;
            IMoniker[] monikers = new IMoniker[1];
            IntPtr numFetched = IntPtr.Zero;

            GetRunningObjectTable(0, out runningObjectTable);
            runningObjectTable.EnumRunning(out monikerEnumerator);
            monikerEnumerator.Reset();

            while (monikerEnumerator.Next(1, monikers, numFetched) == 0)
            {
                IBindCtx ctx;
                CreateBindCtx(0, out ctx);

                string runningObjectName;
                monikers[0].GetDisplayName(ctx, null, out runningObjectName);

                object runningObjectVal;
                runningObjectTable.GetObject(monikers[0], out runningObjectVal);

                result[runningObjectName] = runningObjectVal;
            }

            return result;
        }

        /// <summary>
        /// Get the DTE object for the instance of Visual Studio IDE that has 
        /// the specified solution open.
        /// </summary>
        /// <param name="solutionFile">The absolute filename of the solution</param>
        /// <returns>Corresponding DTE object or null if no such IDE is running</returns>
        public EnvDTE.DTE GetIDEInstance(string solutionID)
        {
            VSDriver vs = new VSDriver();
            Hashtable runningInstances = vs.GetIDEInstances(true);
            IDictionaryEnumerator enumerator = runningInstances.GetEnumerator();

            while (enumerator.MoveNext())
            {
                try
                {
                    string ide = enumerator.Key.ToString();
                    if (ide != null)
                    {
                        if (ide == solutionID)
                        {
                            return (EnvDTE.DTE)enumerator.Value;
                        }
                    }
                }
                catch { }
            }

            return null;
        }
    }
}
